/*
 * Copyright (c) 2008, Martin Novák (http://www.kronos-software.eu)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Martin Novák nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Martin Novák ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Martin Novák BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
if(typeof mn == "undefined" || !mn) {
  var mn = {};
}

mn.dom = {};

mn.dom.getFirstChildTagName = function(parent, tagName)
{
  for(var i = 0; i < parent.childNodes.length; ++i) {
    var child = parent.childNodes[i];
    if(child.tagName == tagName)
      return child;
  }
  return null;
}

mn.forms = {};

mn.forms.Form = function(nameId)
{
  this.form = document.forms[nameId];
  if(!this.form)
    this.form = document.getElementById(nameId);
  if(!this.form)
    throw "Cannot find form";
  this.elements = [];
  this.contentChanged = false;
  
  this.initializeForm();
  this.initializeElements();
}

mn.forms.Form.prototype.initializeForm = function()
{
  var classData = this.preprocessClassData(this.form);
  this.validations = this.gatherValidation(this.form, classData);
}

mn.forms.Form.prototype.initializeElements = function()
{
  var i;
  var radios = [];
  
  for(i = 0; i < this.form.elements.length; ++i) {
    var fi = this.form.elements[i];
    // Process radios separately
    if(fi.tagName === "INPUT" && fi.type === "radio") {
      var radio = radios[fi.name];
      if(!radio) {
        radio = [];
        radios[fi.name] = radio;
      }
      radio[radio.length] = fi;
    }
    else {
      var o = this.processElement(fi);
      this.elements[this.elements.length] = o;
    }
  }
  
  for(var radioName in radios) {
    var radio = radios[radioName];
    var o = this.processElement(radio[0]);
    o.els = radio;
    this.elements[this.elements.length] = o;
  }
}

mn.forms.Form.prototype.hookFieldsChange = function()
{
  var that = this;
  YUI().use('node', function(Y) {
    var handleChange = function(e) {
      that.elementChanged(e);
    };
    for(var i = 0; i < that.elements.length; ++i) {
      var el = that.elements[i];
      if(el.el.tagName === "INPUT" || el.el.tagName === "SELECT" || el.el.tagName === "TEXTAREA") {
        var fooHandle = Y.on("change", handleChange, el.el);
      }
    }
  });
}

mn.forms.Form.prototype.elementChanged = function(el)
{
  el.contentChanged = true;
  this.contentChanged = true;
}

mn.forms.Form.prototype.processElement = function(fi)
{
  var titleContent = this.preprocessTitleTag(fi);
  var classData = this.preprocessClassData(fi);
  var hi = this.guessHint(fi, titleContent);
  var la = this.guessLabel(fi, titleContent);
  var val = this.gatherValidation(fi, classData);
  var hintPos = this.guessHintPosition(fi, classData);
  var errPos = this.guessErrorPosition(fi, classData);
  var o = {
    el: fi,
    id: fi.id,
    hint: hi,
    hintPosition: hintPos,
    errorPosition: errPos,
    label: la,
    validations: val
  };
  this.setupFinalContent(fi, titleContent);
  return o;
}

mn.forms.Form.prototype.preprocessTitleTag = function(fi)
{
  var title = fi.title;
  if(title.length == 0)
    return null;
  var ret = {};

  var titles = title.split(";");
  for(var i = 0; i < titles.length; ++i) {
    var t = titles[i];
    var index = t.indexOf(":");
    if(index > 0) {
      var name = t.substring(0, index).trim();
      var value = t.substring(index).trim();
      ret[name] = value;
    }
    else {
      if(ret['title'])
        return null;
        //throw 'Title is already defined';
      ret['title'] = t;
    }
  }
  
  return ret;
}

mn.forms.Form.prototype.preprocessClassData = function(fi)
{
  var clz = fi.className;
  if(!clz || clz.length == 0)
    return {};

  var ret = {};
  var clzs = clz.split(" ");
  for(var i = 0; i < clzs.length; ++i) {
    var a = clzs[i].split("-");
    var name = a.shift();
    var data = { name: name, params: a };
    ret[data.name] = data;
  }

  return ret;
}

/**
 * Guesses the hint for the form element.
 */
mn.forms.Form.prototype.guessHint = function(fi, titleContent)
{
  if(titleContent && titleContent['hint'])
    return titleContent['hint'];
}

mn.forms.Form.prototype.guessHintPosition = function(fi, classData)
{
  if(classData['form_hint_before'])
    return { pos: 'before' };
  else if(classData['form_hint_after'])
    return { pos: 'after' };
  else if(classData['form_hint_inside']) {
    return { pos: 'inside', id: classData['form_hint_inside'][0] };
  }
  return { pos: 'before' };
}

mn.forms.Form.prototype.guessErrorPosition = function(fi, classData)
{
  if(classData['form_error_before'])
    return { pos: 'before' };
  else if(classData['form_error_after'])
    return { pos: 'after' };
  else if(classData['form_error_inside']) {
    return { pos: 'inside', id: classData['form_error_inside'][0] };
  }
  return { pos: 'after' };
}

mn.forms.Form.prototype.guessLabel = function(fi, titleContent)
{
  if(titleContent && titleContent['label'])
    return titleContent['label'];
  else if(fi.tagName == 'INPUT' && fi.type == 'radio')
    return null;

  // If we cannot find anything so far
  var labels = document.getElementsByTagName("LABEL");
  for(var i = 0; i < labels.length; ++i) {
    var label = labels[i];
    if(fi.id === label.htmlFor) {
      var ret = label.textContent;
      if(ret.charAt(ret.length-1) == ":")
        ret = ret.substring(0, ret.length-1);
      return ret;
    }
  }
  //return "##ERROR - " + fi.id + "##"
  return null;
}

mn.forms.Form.prototype.gatherValidation = function(fi, classData)
{
  if(!classData || classData.length == 0)
    return [];

  var ret = [];
  for(var dataName in classData) {
    if(dataName.indexOf('val_') == 0) {
      var data = classData[dataName];
      ret[ret.length] = { name: data.name, params: data.params };
    }
  }

  return ret;
}

mn.forms.Form.prototype.setupFinalContent = function(fi, titleContent)
{
  if(!titleContent)
    return;
  if(!titleContent['title'])
    return;
  var title = titleContent['title'];
  fi.title = title;
}
